﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;

namespace BesAsm.Stormwater.Swmm5Results
{
  public class ResultsExporter
  {
    private SimulationResults _simResults;
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public TimeSpan OutputInterval { get; set; }
    public string File { get; set; }
    public List<ResultVariablesBase> Elements { get; set; }
    public List<string> Variables { get; set; }

    private BackgroundWorker _worker;

    public ResultsExporter(SimulationResults simResults)
    {
      _simResults = simResults;
      OutputInterval = _simResults.Interval;
    }

    public void WriteResults(string file, List<ResultVariablesBase> elements, List<string> variables)
    {
      File = file;
      Elements = elements;
      Variables = variables;
      WriteResults();
    }    

    public void WriteResults()
    {
      if (Elements.Count == 0) throw new ArgumentException("No elements specified for export");

      StreamWriter writer = null;

      try
      {
        writer = new StreamWriter(File);

        WriteHeader(Elements, Variables, writer);
        WriteBody(Elements, Variables, writer);
      }
      finally
      {
        if (writer != null)
          writer.Close();
      }
    }

    public void PrepareWriteResultsAsync(string file, List<ResultVariablesBase> elements, List<string> variables, BackgroundWorker worker)
    {
      _worker = worker;
      File = file;
      Elements = elements;
      Variables = variables;
      _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
    }

    [System.Diagnostics.DebuggerNonUserCodeAttribute()]
    private void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
      try
      {
        WriteResults();
      }
      catch (Exception ex)
      {
        if (System.IO.File.Exists(File))
          try
          {
            System.IO.File.Delete(File);
          }
          finally
          {
            throw ex;
          }
      }
    }

    private void WriteHeader(List<ResultVariablesBase> elements, List<string> variables, StreamWriter writer)
    {
      writer.Write("DateTime");

      DateTime exportStartDate = StartDate ?? (_simResults.StartDate + _simResults.Interval);
      DateTime exportEndDate = EndDate ?? _simResults.EndDate;

      foreach (ResultVariablesBase ts in elements)
      {
        foreach (ResultVariable rv in ts)
          if (variables.Contains(rv.Name))
          {
            ts.Worker = _worker;
            ts.Load(rv.Name, false, exportStartDate, exportEndDate);
            writer.Write(", {0}_{1}", new object[] { ts.Name, rv.Name });
          }
      }
      elements[0].Close();

      writer.WriteLine();
    }

    private void WriteBody(List<ResultVariablesBase> elements, List<string> variables, StreamWriter writer)
    {
      TimeSpan actualInterval = _simResults.Interval;
      DateTime exportStartDate = StartDate ?? (_simResults.StartDate + actualInterval);
      DateTime exportEndDate = EndDate ?? _simResults.EndDate;

      DateTime firstDate = elements[0][variables[0]].Keys.First();
      if (firstDate > exportStartDate)
        exportStartDate = firstDate;
      DateTime lastDate = elements[0][variables[0]].Keys.Last();
      if (lastDate < exportEndDate)
        exportEndDate = lastDate;

      int outputIntervalsPerActualInterval = (int)(OutputInterval.TotalSeconds / actualInterval.TotalSeconds);

      string dateFormat = GetDateFormat(OutputInterval);

      int i = 0;
      for (DateTime currentDt = exportStartDate; currentDt <= exportEndDate; currentDt += OutputInterval)
      {
        writer.Write(currentDt.ToString(dateFormat));

        try
        {
          WriteResultLine(elements, variables, writer, outputIntervalsPerActualInterval, actualInterval, currentDt);
        }
        catch (Exception ex)
        {
        }
        i += outputIntervalsPerActualInterval;
      }
    }

    private static void WriteResultLine(
      List<ResultVariablesBase> elements, List<string> variables,
      StreamWriter writer, int outputIntervalsPerActualInterval, TimeSpan actualInterval, DateTime currentDt)
    {
      float result;
      DateTime dt = currentDt;
      foreach (ResultVariablesBase ts in elements)
      {
        foreach (ResultVariable rv in ts.Where(p => variables.Contains(p.Name)))
        {
          dt = currentDt;
          result = 0;
          for (int i = 0; i < outputIntervalsPerActualInterval; i++)
          {
            if (rv.ContainsKey(dt))
            {
              result += rv[dt];
              dt += actualInterval;
            }
          }
          result /= outputIntervalsPerActualInterval;
          writer.Write(", {0}", result);
        }
      }
      writer.WriteLine();
    }

    private string GetDateFormat(TimeSpan OutputInterval)
    {
      if (OutputInterval.Hours == 0 && OutputInterval.Minutes == 0 && OutputInterval.Seconds == 0)
        return "MM/dd/yyyy";
      else
        return "MM/dd/yyyy HH:mm:ss";
    }

  }
}
